package org.simpleframework.inject;

import com.jiangli.controller.UserController;
import com.jiangli.service.UserService;
import org.simpleframework.core.BeanContainer;
import org.simpleframework.inject.annotation.Autowired;
import org.simpleframework.util.ClassUtil;

import java.lang.reflect.Field;
import java.util.Set;

/**
 * DI 依赖注入
 *
 * @author 江黎
 * @date 2022-01-30
 */
public class DependencyInjector {

    /**
     * bean容器
     */
    private BeanContainer beanContainer;

    public DependencyInjector() {
        beanContainer = BeanContainer.getInstance();
    }

    /**
     * 执行ioc
     */
    public void doIoc() {
        // 1.遍历bean容器中所有的Class对象
        Set<Class<?>> classSet = beanContainer.getClasses();
        if (classSet == null || classSet.isEmpty()) {
            System.out.println("empty classSet in BeanContainer");
            return;
        }

        for (Class<?> clazz : classSet) {
            // 2.遍历Class对象的所有成员变量
            Field[] fields = clazz.getDeclaredFields();
            if (fields.length == 0) {
                continue;
            }
            for (Field field : fields) {
                // 3.找出被Autowired标记的成员变量
                if (field.isAnnotationPresent(Autowired.class)) {
                    Autowired autowired = field.getAnnotation(Autowired.class);
                    String autowiredValue = autowired.value();
                    // 4.获取这些成员变量的类型
                    Class<?> fieldClass = field.getType();
                    // 5.获取这些成员变量的类型在容器里对应的实例
                    Object fieldValue = getFieldInstance(fieldClass, autowiredValue);
                    if (fieldValue == null) {
                        throw new RuntimeException("unable to inject relevant type, target fieldClass is " + fieldClass.getName() + "autowiredValue is " + autowiredValue);
                    }
                    // 6.通过反射将对应的成员变量实例注入到成员变量所在类的实例里
                    Object targetBean = beanContainer.getBean(clazz);
                    ClassUtil.setField(targetBean, field, fieldValue);
                }
            }
        }


    }

    /**
     * 根据Class在beanContainer中获取其实例或者实现类
     */
    private Object getFieldInstance(Class<?> fieldClass, String autowiredValue) {
        Object fieldValue = beanContainer.getBean(fieldClass);
        if (fieldValue != null) {
            return fieldValue;
        } else {
            Class<?> implementClass = getImplementClass(fieldClass, autowiredValue);
            if (implementClass != null) {
                return beanContainer.getBean(implementClass);
            } else {
                return null;
            }
        }
    }

    /**
     * 获取接口的实现类
     */
    private Class<?> getImplementClass(Class<?> fieldClass, String autowiredValue) {
        Set<Class<?>> classSet = beanContainer.getClassesBySuper(fieldClass);
        if (classSet == null || classSet.isEmpty()) {
            System.out.println("empty classSet in BeanContainer");
            return null;
        } else {
            // 如果@Autowired's value没有值
            if (autowiredValue == null || autowiredValue.length() == 0) {
                if (classSet.size() == 1) {
                    return classSet.iterator().next();
                } else {
                    // 如果多于两个实现类,且用户未指定其中一个实现类,则抛出异常
                    throw new RuntimeException("multiple implement classes for " + fieldClass.getName() + ", please set @Autowired's value to pick one");
                }
            } else {
                for (Class<?> clazz : classSet) {
                    if (autowiredValue.equals(clazz.getSimpleName())) {
                        return clazz;
                    }
                }
            }
        }
        return null;
    }

//    public static void main(String[] args) {
//        BeanContainer beanContainer = BeanContainer.getInstance();
//        beanContainer.loadBeans("com.jiangli");
//        UserController userController = (UserController) beanContainer.getBean(UserController.class);
//        UserService userService = userController.getUserService();
//        // 执行ioc依赖注入前
//        new DependencyInjector().doIoc();
//        // 执行ioc依赖注入后
//        UserService userService1 = userController.getUserService();
//        System.out.println();
//    }
}
